# -*- coding: utf-8 -*-
# @Author : pan
import time
import cv2
import numpy as np

from PySide6.QtCore import (Qt, QTimer, QPropertyAnimation, QEasingCurve,
                            QParallelAnimationGroup, QThread, QMutex,
                            Signal, Slot, QCoreApplication, QDate, QDateTime,
                            QLocale, QMetaObject, QObject, QPoint, QRect,
                            QSize, QTime, QUrl, QAbstractAnimation)
from PySide6.QtGui import (QPixmap, QPainter, QColor, QFontMetrics, QPen,
                           QWheelEvent, QCursor, QAction, QImage, QPainterPath,
                           QBrush, QConicalGradient, QFont, QFontDatabase,
                           QGradient, QIcon, QKeySequence, QLinearGradient,
                           QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QWidget, QLabel, QMenu, QDialog,
                               QVBoxLayout, QLineEdit, QPushButton, QMessageBox,
                               QInputDialog, QFrame, QHBoxLayout, QLayout,
                               QSizePolicy, QSpacerItem)



# 提示组件
class Toast(QWidget):
    def __init__(
        self,
        text: str,
        duration: int = 3000,
        parent: QWidget = None,
    ):
        super().__init__(parent)
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_TranslucentBackground, True)
        self.duration = duration

        label = QLabel(self)
        label.setText(text)
        label.setStyleSheet("""
            background-color: rgba(60, 179, 113, 0.8);
            color: white;
            font-size: 16px;
            padding: 12px;
            border-radius: 4px;
        """)
        label.setAlignment(Qt.AlignCenter)

        fm = QFontMetrics(label.font())
        width = fm.boundingRect(text).width() + 80

        # 高度与宽度
        label.setFixedWidth(width)
        label.setFixedHeight(40)

        self.setGeometry(*self.calculatePosition(label.sizeHint()))


        # x = self.parent.x() + self.parent().width() / 2 - label.width()/2
        # y = self.parent.y() + self.parent().height() / 2 - label.height()/2
        #
        # print(f"x:{x},y:{y}", f"width:{self.parent().width()},height:{self.parent().height()}")
        # self.setGeometry(x,y,500,500)
        self.fadeIn()

        self.animationTimer = QTimer()

        self.animationTimer.timeout.connect(self.fadeOut)
        self.animationTimer.start(self.duration)

    def calculatePosition(self, sizeHint):
        if self.parent() is not None:
            # 如果存在父窗口，计算使Toast窗口相对于父窗口居中的位置
            x = 12
            y = 10

        else:
            # 如果没有父窗口，计算使Toast窗口相对于屏幕居中的位置
            desktopRect = QApplication.primaryScreen().availableGeometry()
            x = (desktopRect.width() - sizeHint.width()) // 2
            y = (desktopRect.height() - sizeHint.height()) // 2
        return x, y, sizeHint.width(), sizeHint.height()

    def fadeIn(self):
        # 创建并设置淡入动画
        fadeInAnimation = QPropertyAnimation(self, b"windowOpacity", self)
        fadeInAnimation.setStartValue(0)
        fadeInAnimation.setEndValue(1)
        fadeInAnimation.setDuration(500)
        fadeInAnimation.finished.connect(lambda: print('加载成功'))
        # 启动淡入动画
        fadeInAnimation.start()

        print('in')

    # 淡出动画
    def fadeOut(self):
        print('out')
        # 停止计时器
        self.animationTimer.stop()
        # 断开计时器的超时信号与当前方法的连接
        self.animationTimer.timeout.disconnect(self.fadeOut)

        # 创建并设置并行动画组
        self.parallelAnimation = QParallelAnimationGroup()

        # 创建并设置不透明度动画
        self.opacityAnimation = QPropertyAnimation(self, b"windowOpacity")
        self.opacityAnimation.setStartValue(1.0)
        self.opacityAnimation.setEndValue(0.0)
        self.opacityAnimation.setDuration(500)

        # 创建并设置位置动画
        self.yAnimation = QPropertyAnimation(self, b"geometry")
        targetY = self.y() - 10
        self.yAnimation.setStartValue(self.geometry())
        self.yAnimation.setEndValue(self.geometry().translated(0, targetY))
        self.yAnimation.setDuration(500)
        self.yAnimation.setEasingCurve(QEasingCurve.OutCubic)

        # 将动画添加到并行动画组中
        self.parallelAnimation.addAnimation(self.opacityAnimation)
        self.parallelAnimation.addAnimation(self.yAnimation)

        # 连接并行动画组的完成信号与关闭窗口的槽
        self.parallelAnimation.finished.connect(self.close)

        # 启动动画组
        self.parallelAnimation.start(QAbstractAnimation.DeleteWhenStopped)


    def paintEvent(self, event):
        painter = QPainter(self)
        painter.fillRect(self.rect(), QColor(0, 0, 0, 0))

    def mousePressEvent(self, event):
        pass

# 弹窗 输入框
class Ui_InputColor(QWidget):
    textEntered = Signal(str)  # 定义一个带有字符串参数的信号

    def __init__(self):
        super().__init__()
        self.drag_position = None


    def setupUi(self, Form):

        Form.setObjectName("Form")  # 直接设置对象名称
        Form.resize(200, 70)

        # 设置背景颜色和透明度
        Form.setWindowFlags(Qt.FramelessWindowHint)  # 设置无边框窗口
        Form.setAttribute(Qt.WA_TranslucentBackground)  # 设置窗口透明

        self.bg_gray = QFrame(Form)
        self.bg_gray.setObjectName(u"bg_gray")
        self.bg_gray.setGeometry(QRect(0, 0, 200, 65))
        self.bg_gray.setStyleSheet(u".QFrame{\n"
"	background-color: rgb(234,236,243);\n"
"	border-radius:5px;\n"
"}")
        self.bg_gray.setFrameShape(QFrame.StyledPanel)
        self.bg_gray.setFrameShadow(QFrame.Raised)
        self.verticalLayout_3 = QVBoxLayout(self.bg_gray)
        self.verticalLayout_3.setSpacing(0)
        self.verticalLayout_3.setObjectName(u"verticalLayout_3")
        self.verticalLayout_3.setContentsMargins(10, 11, 4, 11)
        self.verticalLayout_2 = QVBoxLayout()
        # 设置右侧边距为5px
        self.verticalLayout_2.setContentsMargins(0, 0, 7, 0)

        self.verticalLayout_2.setObjectName(u"verticalLayout_2")
        self.window_top = QHBoxLayout()
        self.window_top.setSpacing(10)
        self.window_top.setObjectName(u"window_top")
        self.window_top.setSizeConstraint(QLayout.SetFixedSize)
        self.window_top.setContentsMargins(0, 0, 0, 0)
        self.text_title = QLabel(self.bg_gray)
        self.text_title.setObjectName(u"text_title")
        self.text_title.setStyleSheet(u"    font-size: 9pt;\n"
"    font-family: \"\u5fae\u8f6f\u96c5\u9ed1\";\n"
"    color: #333;\n"
"font-weight: bold;")

        self.window_top.addWidget(self.text_title)

        self.h_spacer = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)

        self.window_top.addItem(self.h_spacer)

        self.btn_min = QPushButton(self.bg_gray)
        self.btn_min.setObjectName(u"btn_min")
        sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.btn_min.sizePolicy().hasHeightForWidth())
        self.btn_min.setSizePolicy(sizePolicy)
        self.btn_min.setMaximumSize(QSize(10, 10))
        self.btn_min.setStyleSheet(u"QPushButton {\n"
"    background-color: #07BB2C;\n"
"    border: 2px solid #07BB2C;\n"
"    color: #3498db;\n"
"    padding: 1px;\n"
"    border-radius: 5px;\n"
"}\n"
" \n"
"QPushButton:hover {\n"
"    background-color: #09ED3A;\n"
"    color: #ffffff;\n"
"}")

        self.window_top.addWidget(self.btn_min)

        self.btn_max = QPushButton(self.bg_gray)
        self.btn_max.setObjectName(u"btn_max")
        self.btn_max.setMaximumSize(QSize(10, 10))
        self.btn_max.setStyleSheet(u"QPushButton {\n"
"    background-color: #FFB206;\n"
"    border: 2px solid #FFB206;\n"
"    color: #3498db;\n"
"    padding: 1px;\n"
"    border-radius: 5px;\n"
"}\n"
" \n"
"QPushButton:hover {\n"
"    background-color: #FFC033;\n"
"    color: #ffffff;\n"
"}")

        self.window_top.addWidget(self.btn_max)

        self.btn_stop = QPushButton(self.bg_gray)
        self.btn_stop.setObjectName(u"btn_stop")

        self.btn_stop.clicked.connect(self.close)  # 关联关闭窗口事件

        self.btn_stop.setMaximumSize(QSize(10, 10))
        self.btn_stop.setStyleSheet(u"\n"
"\n"
"QPushButton {\n"
"    background-color: #EE514A;\n"
"    border: 2px solid #EE514A;\n"
"    color: #3498db;\n"
"    padding: 1px;\n"
"    border-radius: 5;\n"
"}\n"
" \n"
"QPushButton:hover {\n"
"    background-color: #F1756F;\n"
"    color: #ffffff;\n"
"}")

        self.window_top.addWidget(self.btn_stop)


        self.verticalLayout_2.addLayout(self.window_top)

        self.horizontalLayout = QHBoxLayout()
        self.horizontalLayout.setObjectName(u"horizontalLayout")
        self.lineEdit = QLineEdit(self.bg_gray)
        self.lineEdit.setObjectName(u"lineEdit")

        self.horizontalLayout.addWidget(self.lineEdit)

        self.b_submit = QPushButton(self.bg_gray)

        self.b_submit.clicked.connect(self.emitSignal)  # 点击按钮时连接到发射信号的方法

        self.b_submit.setObjectName(u"b_submit")
        self.b_submit.setMinimumSize(QSize(0, 0))
        self.b_submit.setStyleSheet(u"            QPushButton {\n"
"                border-style: solid;\n"
"                border-width: 2px;\n"
"                border-radius: 15px;\n"
"                border-color: rgb(88, 180, 107);\n"
"                font-size: 7pt;\n"
"                font-weight: bold;\n"
"                padding: 2px;\n"
"                background-color: rgb(88, 180, 107);\n"
"                color: rgb(255, 255, 255);\n"
"            }\n"
"            QPushButton:hover {\n"
"                border-color: rgb(100, 100, 100);\n"
"                background-color: rgb(183, 255, 189);\n"
"				color: rgb(88, 180, 107);\n"
"            }\n"
"            QPushButton:pressed {\n"
"                border-color: rgb(42, 42, 42);\n"
"                background-color: rgb(160, 255, 163);\n"
"				color: rgb(88, 180, 107);\n"
"            }")

        self.horizontalLayout.addWidget(self.b_submit)


        self.verticalLayout_2.addLayout(self.horizontalLayout)


        self.verticalLayout_3.addLayout(self.verticalLayout_2)


        self.retranslateUi(Form)

        QMetaObject.connectSlotsByName(Form)
    # setupUi

    @Slot()
    def emitSignal(self):
        text = self.lineEdit.text()
        self.textEntered.emit(text)  # 发射信号，并传递lineEdit中的文本

    def retranslateUi(self, Form):
        Form.setWindowTitle(QCoreApplication.translate("Form", u"Form", None))
        self.text_title.setText(QCoreApplication.translate("Form", u"\u8bf7\u8f93\u5165\u8272\u53f7", None))
        self.btn_min.setText("")
        self.btn_max.setText("")
        self.btn_stop.setText("")
        self.lineEdit.setPlaceholderText(QCoreApplication.translate("Form", u"255,255,255", None))
        self.b_submit.setText(QCoreApplication.translate("Form", u"\u786e\u8ba4", None))
    # retranslateUi
    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton and self.drag_position:
            self.move(event.globalPos() - self.drag_position)
            event.accept()

    def mouseReleaseEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_position = None
            event.accept()


# 摄像头检测类
class Camera:
    def __init__(self, cam_preset_num=5):
        self.cam_preset_num = cam_preset_num

    def get_cam_num(self):
        cnt = 0
        devices = []
        for device in range(0, self.cam_preset_num):
            stream = cv2.VideoCapture(device, cv2.CAP_DSHOW)
            grabbed = stream.grab()
            stream.release()
            if not grabbed:
                continue
            else:
                cnt = cnt + 1
                devices.append(device)
        return cnt, devices

# 摄像头线程
class CameraThread(QThread):
    change_pixmap_signal = Signal(np.ndarray)

    def __init__(self, device_id=0):
        super().__init__()
        self.device_id = device_id
        self.is_paused = False
        self.mutex = QMutex()
        self.exiting = False
        self.cap = None

    def run(self):
        self.cap = cv2.VideoCapture(self.device_id)
        while not self.exiting:
            self.mutex.lock()
            if not self.is_paused:
                ret, frame = self.cap.read()
                if ret:
                    self.change_pixmap_signal.emit(frame)
                else:
                    break
            self.mutex.unlock()

        self.cap.release()
        self.cap = None

    def set_device_id(self, device_id):
        self.device_id = device_id

    def pause(self):
        self.mutex.lock()
        self.is_paused = True
        self.mutex.unlock()

    def resume(self):
        self.mutex.lock()
        self.is_paused = False
        self.mutex.unlock()

    def stop(self):
        self.exiting = True
        self.wait()

    def set_device_id(self, device_id):
        self.device_id = device_id

    def stop(self):
        self.exiting = True

# 主窗口
class MyWindow(QWidget):

    def __init__(
            self,
            text: str,
            parent: QWidget = None,
    ):
        super().__init__(parent)
        # 设置窗口属性
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_TranslucentBackground, True)

        # 窗口大小
        self.size = 200
        self.color = "255,255,255"

        # 子线程
        self.camera_thread = CameraThread()
        self.camera_thread.change_pixmap_signal.connect(self.image_data_slot)
        self.camera_thread.start()

        self.select_camera = False
        self.input_color_window = None

        # 创建标签并设置样式
        self.label = QLabel(self)
        self.label.setText(text)
        self.reset_style()  # 样式刷新
        self.label.setAlignment(Qt.AlignCenter)
        # 设置标签尺寸
        self.label.setAlignment(Qt.AlignCenter)
        self.label.setFixedSize(self.size, self.size)  # 设置标签的宽度和高度

        # 设置窗口位置
        self.setGeometry(*self.calculatePosition(self.label.sizeHint()))

        self.fadeIn()

    def fadeIn(self):
        # 创建并设置淡入动画
        fadeInAnimation = QPropertyAnimation(self, b"windowOpacity", self)
        fadeInAnimation.setStartValue(0)
        fadeInAnimation.setEndValue(1)
        fadeInAnimation.setDuration(500)
        fadeInAnimation.finished.connect(lambda: print('窗口 加载成功'))
        # 启动淡入动画
        fadeInAnimation.start()

    # 出现在屏幕居中的位置
    def calculatePosition(self, sizeHint):
        desktopRect = QApplication.primaryScreen().availableGeometry()
        x = (desktopRect.width() - sizeHint.width()) // 2
        y = (desktopRect.height() - sizeHint.height()) // 2
        return x, y, sizeHint.width(), sizeHint.height()

    def paintEvent(self, event):
        painter = QPainter(self)
        painter.fillRect(self.rect(), QColor(0, 0, 0, 0))

    def image_data_slot(self, image_data):

        if self.select_camera is True:
            self.label.clear()
            self.label.setText('请重新选择摄像头')
            return

        self.image = cv2.cvtColor(image_data, cv2.COLOR_BGR2RGB)
        height, width, channel = self.image.shape
        bytesPerLine = 3 * width
        image = QImage(self.image.data, width, height, bytesPerLine, QImage.Format_RGB888)

        # 将图片先裁剪为正方形
        width = image.width()
        height = image.height()
        size = min(width, height)
        square_image = image.copy((width - size) // 2, (height - size) // 2, size, size)

        # 计算缩放比例
        new_size = self.size - 10
        label_size = self.label.sizeHint()
        scaled_image = square_image.scaled(new_size, new_size, Qt.KeepAspectRatio)

        # 将 QImage 转换为 QPixmap
        pixmap = QPixmap.fromImage(scaled_image)

        # 创建一个圆形路径
        path = QPainterPath()
        path.addEllipse(0, 0, new_size, new_size)

        # 创建一个与 scaled_image 大小相同的的 QPixmap
        result_pixmap = QPixmap(new_size, new_size)
        result_pixmap.fill(Qt.transparent)

        # 在 result_pixmap 上绘制圆形图像
        painter = QPainter(result_pixmap)
        painter.setClipPath(path)  # 设置裁剪路径
        painter.setRenderHint(QPainter.Antialiasing)  # 设置抗锯齿
        painter.drawPixmap(0, 0, pixmap)  # 绘制原始图像

        painter.end()
        # 在 label 上显示圆形图像
        self.label.setPixmap(result_pixmap)

    def reset_style(self):
        self.label.setStyleSheet(f"""
        font-family: Arial, sans-serif; /* 设置字体 */
        font-size: 12pt; /* 设置字体大小 */

        background-color: rgb({self.color}); /* 设置背景颜色为红色 */
        padding: 0px; 
        border-radius: {self.size / 2}; /* 设置圆角大小为宽度的一半 */
                """)

    def get_camera_devices(self):
        camera_info = []
        for i in range(10):  # 假设最多有10个摄像头设备
            cap = cv2.VideoCapture(i)
            if cap.isOpened():
                _, _ = cap.read()
                camera_info.append(f"Camera {i}")
                cap.release()
        return camera_info

    def mousePressEvent(self, event):
        if event.button() == Qt.LeftButton:
            self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
            event.accept()

        # 右键 打开菜单
        if event.button() == Qt.RightButton:

            menu = QMenu(self)
            set_color_action = menu.addAction("设置边框色")
            set_camera_action = menu.addAction("选择摄像头")
            set_quit_action = menu.addAction("退出")
            action = menu.exec_(self.mapToGlobal(event.pos()))
            if action == set_color_action:
                # 打开窗口 Ui_InputColor
                self.open_input_color_window()
            # 获取目前所有摄像头的设备号，然后把设备号弄成一个菜单，让用户选择
            if action == set_camera_action:
                self.select_camera = True

                # 如果有摄像头在运行
                if self.camera_thread.cap is not None:
                    # 这里给我绘制一个标签，等待2s再消失
                    self.toast = Toast("正在关闭摄像头中", 1000, self)
                    self.toast.show()
                    time.sleep(1)
                    self.camera_thread.stop()  # 停止摄像头播放
                    return

                # 检测摄像头可用设备，把设备号弄成一个菜单，让用户选择
                # 获取本地摄像头数量
                _, cams = Camera().get_cam_num()
                popMenu = QMenu()
                popMenu.setFixedWidth(110)
                popMenu.setStyleSheet('''
                    QMenu {
                    font-size: 9px;
                    font-family: "Microsoft YaHei UI";
                    font-weight: light;
                    color:white;
                    padding-left: 5px;
                    padding-right: 5px;
                    padding-top: 4px;
                    padding-bottom: 4px;
                    border-style: solid;
                    border-width: 0px;
                    border-color: rgba(255, 212, 255, 255);
                    border-radius: 3px;
                    background-color: rgba(16,155,226,50);
                    }''')
                for cam in cams:
                    action = QAction(f'{cam} 号摄像头')
                    popMenu.addAction(action)
                pos = QCursor.pos()
                action = popMenu.exec(pos)

                # 设置摄像头来源
                if action:
                    str_temp = ''
                    selected_stream_source = str_temp.join(filter(str.isdigit, action.text()))  # 获取摄像头号，去除非数字字符
                    self.toast = Toast(f"摄像头设备是:{selected_stream_source}", 1000, self)
                    self.toast.show()
                    self.select_camera = False

                    self.camera_thread = CameraThread()
                    self.camera_thread.device_id = int(selected_stream_source)
                    self.camera_thread.change_pixmap_signal.connect(self.image_data_slot)
                    self.camera_thread.start()

            # 退出
            if action == set_quit_action:
                self.close()

    # 颜色设置
    def colorEntered(self, text):
        # TODO 数据处理
        self.color = text
        self.reset_style()
        self.input_color_window.close()

    #  打开窗口 Ui_InputColor
    def open_input_color_window(self):
        if self.input_color_window is None:  # 只有当窗口对象尚未创建时才创建它
            self.input_color_window = Ui_InputColor()
            self.input_color_window.setupUi(self.input_color_window)
            self.input_color_window.setWindowFlag(Qt.FramelessWindowHint)  # 设置窗口标志：隐藏窗口边框
            self.input_color_window.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)  # 设置窗口标志位

            self.input_color_window.textEntered.connect(self.colorEntered)  # 连接子窗口的信号和父窗口的槽

        # 窗口显示在父窗口的中心
        self.input_color_window.move(
            self.x() + (self.width() - self.input_color_window.width()) / 2,
            self.y() + (self.height() - self.input_color_window.height()) / 2
        )
        self.input_color_window.show()

    def mouseMoveEvent(self, event):

        # 左键 移动创建
        if event.buttons() == Qt.LeftButton:
            self.move(event.globalPos() - self.drag_position)
            event.accept()

    def mouseReleaseEvent(self, event):
        self.drag_position = None

    # 窗口大小变换
    def row_event(self, up=0, down=0):
        self.size = self.size + up
        self.size = self.size - down

        self.reset_style()  # 样式刷新

        self.label.setFixedSize(self.size, self.size)  # 设置标签的宽度和高度
        self.setFixedSize(self.size, self.size)

    def event(self, event):
        if event.type() == QWheelEvent.Wheel:
            # 阻止默认的滚动行为
            event.ignore()

            # 获取滚动方向和距离
            delta = event.angleDelta().y() / 120

            # 根据滚动方向和距离执行相应操作
            if delta < 0:
                # 向下滚动(触发两次 因为背景上有bug
                self.row_event(down=1)
                self.row_event(down=1)
            else:
                # 向上滚动(触发两次 因为背景上有bug
                self.row_event(up=1)
                self.row_event(up=1)

            return True
        return super().event(event)


if __name__ == "__main__":
    app = QApplication([])

    window = MyWindow("Camera Software By Pan")
    window.show()

    app.exec()
